﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Tile_Engine;

namespace AGrandVenture
{
    class Enemy : GameObject
    {
        #region Declarations
        public Sprite EnemyBase;
        public Sprite EnemyClaws;
        public Vector2 currentTargetSquare;
        public bool Destroyed = false;
        public bool Aggroed = false;
        private static int MaxAggroRange = 1000;
        private int collisionRadius = 28;
        private string name;
        private int hp;
        private int xpValue;
        private int enemyLevel;
        private float moveSpeed;
        private int damageDealtOnContactWithPlayer;
        private int damageTakenWhenHitWithPlayerSpell;
        private float chanceToDropLoot;
        private bool hasClaws = true;
        private bool hasRangedAttack = false;
        private bool hasMeleeAttack = false;
        private bool currentlyAttacking = false;
        private int maxAttackRange = 0;
        private bool needsSpriteFlip = false;
        private bool needsSpriteRotation = true;
        #endregion

        #region Properties

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public int HP
        {
            get { return hp; }
            set { hp = value; }
        }

        public int XP_Value
        {
            get { return xpValue; }
            set { xpValue = value; }
        }

        public int Level
        {
            get { return enemyLevel; }
            set { enemyLevel = value; }
        }

        public float MoveSpeed
        {
            get { return moveSpeed; }
            set { moveSpeed = value; }
        }

        public bool HasClaws
        {
            get { return hasClaws; }
            set { hasClaws = value; }
        }

        public bool HasRangedAttack
        {
            get { return hasRangedAttack; }
            set { hasRangedAttack = value; }
        }

        public bool HasMeleeAttack
        {
            get { return hasMeleeAttack; }
            set { hasMeleeAttack = value; }
        }

        public bool CurrentlyAttacking
        {
            get { return currentlyAttacking; }
            set { currentlyAttacking = value; }
        }

        public int MaxAttackRange
        {
            get { return maxAttackRange; }
            set { maxAttackRange = value; }
        }

        public int DamageDealt
        {
            get { return damageDealtOnContactWithPlayer; }
            set { damageDealtOnContactWithPlayer = value; }
        }

        public int DamageTaken
        {
            get { return damageTakenWhenHitWithPlayerSpell; }
            set { damageTakenWhenHitWithPlayerSpell = value; }
        }

        public float ChanceToDropLoot
        {
            get { return chanceToDropLoot; }
            set { chanceToDropLoot = value; }
        }

        public bool NeedsToFlip
        {
            get { return needsSpriteFlip; }
            set { needsSpriteFlip = value; }
        }

        public bool NeedsToRotate
        {
            get { return needsSpriteRotation; }
            set { needsSpriteRotation = value; }
        }

        public Vector2 WorldLocation
        {
            get { return EnemyBase.WorldLocation; }
            set { EnemyBase.WorldLocation = value; if (HasClaws) EnemyClaws.WorldLocation = value; }
        }

        #endregion

        #region Constructor

        public Enemy(
            string name,
            Vector2 worldLocation,
            Texture2D texture,
            Rectangle initialFrame
            )
        {

            this.name = name;
            EnemyBase = new Sprite(
                worldLocation,
                texture,
                initialFrame,
                Vector2.Zero,
                0.85f);

            EnemyBase.CollisionRadius = collisionRadius;

            if (HasClaws)
            {
                Rectangle turretFrame = initialFrame;

                turretFrame.Offset(0, initialFrame.Height);
                //EnemyClaws = new Sprite(
                //    new Vector2(0, 0),
                //    texture,
                //    turretFrame,
                //    Vector2.Zero,
                //    0.85f);
                EnemyClaws = EnemyBase;
            }
        }
        #endregion

        #region Public Methods
        public void Update(GameTime gameTime)
        {
            if (!Destroyed && !CurrentlyAttacking)
            {
                Vector2 direction = Vector2.Zero;
                if (!Aggroed)
                {
                    if (withinAggroRange() && hasLineOfSight())
                        Aggroed = true;
                    else
                    {
                        direction = wanderAimlessly();
                        direction.Normalize();
                        EnemyBase.Velocity = direction * MoveSpeed;
                        EnemyBase.Update(gameTime);
                    }
                }
                if (Aggroed)
                {
                    direction = determineMoveDirection();
                    direction.Normalize();

                    if (hasLineOfSight())
                    {
                        if (HasRangedAttack && withinRangedAttackRange())
                        {
                            performRangedAttack();
                        }
                        else if (HasMeleeAttack && withinMeleeRange())
                        {
                            performMeleeAttack();
                        }
                    }

                    EnemyBase.Velocity = direction * MoveSpeed;
                    if (this.NeedsToRotate)
                        EnemyBase.RotateTo(direction);
                    EnemyBase.Update(gameTime);

                    if (HasClaws)
                    {
                        Vector2 directionToPlayer = Player.BaseSprite.WorldCentre - EnemyBase.WorldCentre;
                        directionToPlayer.Normalize();
                        EnemyClaws.WorldLocation = EnemyBase.WorldLocation;
                        EnemyClaws.RotateTo(directionToPlayer);
                    }
                }

                //Keeps the enemy facing towards the player at all times for directional sprites
                if (direction != Vector2.Zero && this.NeedsToFlip)
                {
                    if (direction.X < 0)
                        EnemyBase.FlipFramesX(true);
                    else
                        EnemyBase.FlipFramesX(false);

                    //Keeps enemy from rotating upside down
                    if (this.NeedsToRotate)
                    {
                        if (Math.Abs(EnemyBase.Rotation) > 1.8)
                            EnemyBase.FlipFramesY(true);
                        else
                            EnemyBase.FlipFramesY(false);
                    }
                }
            }
            else if (CurrentlyAttacking && EnemyBase.PlayedAllFramesOnce)
                endOfAttack();
            else
                EnemyBase.Update(gameTime);
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            if (!Destroyed)
            {
                EnemyBase.Draw(spriteBatch);

                if (HasClaws)
                    EnemyClaws.Draw(spriteBatch);
            }
        }

        public void DrawEnemyShadow(SpriteBatch spriteBatch, Vector2 lightVector, double size)
        {
            if (!Destroyed)
            {
                EnemyBase.DrawShadow(spriteBatch, lightVector, size);

                if (HasClaws)
                    EnemyClaws.DrawShadow(spriteBatch, lightVector, size);
            }
        }

        public void checkLoot()
        {
            Random random = new Random();
            double result = random.NextDouble();

            if (this.ChanceToDropLoot >= result)
            {
                LootItem newLoot = new LootItem(SeedUtility.newSeed(Player.Character.Level+ random.Next(0, 7)), WorldLocation);
                LootManager.addLoot(newLoot);
                MessageWindow.AddMessage(Name + " dropped a " + newLoot.Item.Name + "!", Color.LightSkyBlue);
                TextZoomManager.AddTextZoom(newLoot.Item.Name, Color.LightSkyBlue, new Vector2(this.EnemyBase.ScreenLocation.X - 10, this.EnemyBase.ScreenLocation.Y + 30));
            }
        }

        #endregion

        #region AI Methods
        private Vector2 determineMoveDirection()
        {
            if ((currentTargetSquare == Vector2.Zero) || reachedTargetSquare())
            {
                currentTargetSquare = getNewTargetSquare();
            }

            Vector2 squareCentre = TileMap.GetCellCentre(currentTargetSquare);

            return squareCentre - EnemyBase.WorldCentre;
        }

        private Vector2 wanderAimlessly()
        {
            if ((currentTargetSquare == Vector2.Zero) || reachedTargetSquare())
            {
                //Determine direction to look first
                Random random = new Random();
                int DULR = random.Next(0, 4);

                //Record current location, and player location
                Vector2 currLocation = TileMap.GetCellByPixel(this.EnemyBase.ScreenCentre);
                Vector2 playerref = TileMap.GetCellByPixel(Player.BaseSprite.ScreenCentre);
                
                MapSquare resultSquare = null;

               
                switch (DULR % 4)//0-3 result
                {
                    case 0://Down, (x,y+1)
                        resultSquare = TileMap.GetMapSquareAtCell((int)currLocation.X, (int)currLocation.Y + 1);
                        if (resultSquare != null && resultSquare.Passable)
                            currentTargetSquare = TileMap.GetCellByPixel(new Vector2(this.EnemyBase.WorldCentre.X, (this.EnemyBase.WorldCentre.Y + TileMap.TileHeight)));
                        break;

                    case 1://Up, (x,y-1)
                        resultSquare = TileMap.GetMapSquareAtCell((int)currLocation.X, (int)currLocation.Y - 1);
                        if (resultSquare != null && resultSquare.Passable)
                            currentTargetSquare = TileMap.GetCellByPixel(new Vector2(this.EnemyBase.WorldCentre.X, (this.EnemyBase.WorldCentre.Y - TileMap.TileHeight)));
                        break;

                    case 2://Left, (x-1,y)
                        resultSquare = TileMap.GetMapSquareAtCell((int)currLocation.X - 1, (int)currLocation.Y);
                        if (resultSquare != null && resultSquare.Passable)
                            currentTargetSquare = TileMap.GetCellByPixel(new Vector2((this.EnemyBase.WorldCentre.X - TileMap.TileWidth), this.EnemyBase.WorldCentre.Y));
                        break;

                    case 3://Right, (x+1,y)
                        resultSquare = TileMap.GetMapSquareAtCell((int)currLocation.X + 1, (int)currLocation.Y);
                        if (resultSquare != null && resultSquare.Passable)
                            currentTargetSquare = TileMap.GetCellByPixel(new Vector2((this.EnemyBase.WorldCentre.X + TileMap.TileWidth), this.EnemyBase.WorldCentre.Y));
                        break;
                }

               
            }
            Vector2 squareCentre = TileMap.GetCellCentre(currentTargetSquare);

            if (currentTargetSquare == Vector2.Zero)
                return EnemyBase.WorldCentre;
            else
                return squareCentre - EnemyBase.WorldCentre;
        }

        private bool withinAggroRange()
        {
            float distanceFromPlayer = Vector2.Distance(EnemyBase.WorldCentre, Player.BaseSprite.WorldCentre);

            if (distanceFromPlayer <= MaxAggroRange)
                return true;
            else
                return false;
        }

        private bool withinRangedAttackRange()
        {
            float distanceFromPlayer = Vector2.Distance(EnemyBase.WorldCentre, Player.BaseSprite.WorldCentre);

            if (distanceFromPlayer <= maxAttackRange)
                return true;
            else
                return false;
        }

        private bool withinMeleeRange()
        {
            float distanceFromPlayer = Vector2.Distance(EnemyBase.WorldCentre, Player.BaseSprite.WorldCentre);

            if (distanceFromPlayer <= 32)
                return true;
            else
                return false;
        }



        private bool hasLineOfSight()
        {
            Vector2 a = EnemyBase.ScreenCentre;
            Vector2 b = Player.BaseSprite.ScreenCentre;
            Vector2 diff = a - b;
            MapSquare currSquare = null;
            float len = 0f;

            len = diff.Length() / 8;

            for (int i = 0; i < len; i++)
            {
                currSquare = TileMap.GetMapSquareAtPixel(a);
                if (currSquare != null && !currSquare.Passable)
                    return false;

                a.X -= diff.X / len;
                a.Y -= diff.Y / len;
            }

            return true;
        }

        private bool reachedTargetSquare()
        {
            return (Vector2.Distance(EnemyBase.WorldCentre, TileMap.GetCellCentre(currentTargetSquare)) <= 2);
        }

        private Vector2 getNewTargetSquare()
        {
            List<Vector2> path = PathFinder.FindPath(TileMap.GetCellByPixel(EnemyBase.WorldCentre), TileMap.GetCellByPixel(Player.BaseSprite.WorldCentre));

            if (path != null && path.Count > 1)
            {
                return new Vector2(path[1].X, path[1].Y);
            }
            else
            {
                return TileMap.GetCellByPixel(Player.BaseSprite.WorldCentre);
            }
        }
        #endregion

        #region Virtual Methods
        public virtual void performRangedAttack() {return; }
        public virtual void performMeleeAttack() { return; }
        public virtual void endOfAttack() { return; }
        #endregion
    }
}

